package cn.ratel.route;

import cn.ratel.kit.CookieKit;
import cn.ratel.kit.DESKit;
import cn.ratel.kit.StrKit;
import cn.ratel.route.injector.Ip;
import cn.ratel.route.injector.LoginUID;
import cn.ratel.route.injector.UploadedFile;
import com.google.common.base.Preconditions;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * author iver
 */
enum DefaultInjectors implements ActionParamProvider.Injector {

	HTTP_SERVLET_REQUEST {
		@Override
		public HttpServletRequest inject(HttpServletRequest req, HttpServletResponse res, String paramName) {
			return req;
		}
	},

	HTTP_SERVLET_RESPONSE {
		@Override
		public HttpServletResponse inject(HttpServletRequest req, HttpServletResponse res, String paramName) {
			return res;
		}
	},

	IP {
		@Override
		public Ip inject(HttpServletRequest req, HttpServletResponse res, String paramName) {
			String ip = req.getHeader("X-Requested-For");
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getHeader("X-Forwarded-For");
			}
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getHeader("Proxy-Client-IP");
			}
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getHeader("WL-Proxy-Client-IP");
			}
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getHeader("HTTP_CLIENT_IP");
			}
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getHeader("HTTP_X_FORWARDED_FOR");
			}
			if (StrKit.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = req.getRemoteAddr();
			}
			return new Ip(ip);
		}
	},

	LOGIN_UID {
		private String userCookieKey = "uid";
		private String userCookieSecret;

		public void setUserCookieKey(String userCookieKey) {
			this.userCookieKey = userCookieKey;
		}

		public void setUserCookieSecret(String userCookieSecret) {
			this.userCookieSecret = userCookieSecret;
		}

		@Override
		public LoginUID inject(HttpServletRequest req, HttpServletResponse res, String paramName) {
			Preconditions.checkNotNull(userCookieSecret, "userCookieSecret can not be empty");
			String cookieKey = userCookieKey;
			// 获取cookie信息
			String userCookie = CookieKit.get(req, cookieKey);
			// 1.cookie为空，直接清除
			if (StrKit.isBlank(userCookie)) {
				CookieKit.remove(res, cookieKey);
				return null;
			}
			// 2.解密cookie
			String cookieInfo = null;
			// cookie 私钥
			String secret = userCookieSecret;
			try {

				cookieInfo = new DESKit(secret).decryptString(userCookie);
			} catch (RuntimeException e) {
				// ignore
			}
			// 3.异常或解密问题，直接清除cookie信息
			if (StrKit.isBlank(cookieInfo)) {
				CookieKit.remove(res, cookieKey);
				return null;
			}
			String[] userInfo = cookieInfo.split("~");
			// 4.规则不匹配
			if (userInfo.length < 3) {
				CookieKit.remove(res, cookieKey);
				return null;
			}
			String userId = userInfo[0];
			String oldTime = userInfo[1];
			String maxAge = userInfo[2];
			// 5.判定时间区间，超时的cookie清理掉
			if (!"-1".equals(maxAge)) {
				long now = System.currentTimeMillis();
				long time = Long.parseLong(oldTime) + (Long.parseLong(maxAge) * 1000);
				if (time <= now) {
					CookieKit.remove(res, cookieKey);
					return null;
				}
			}
			return new LoginUID(Integer.parseInt(userId));
		}
	},

	FILES {
		private String uploadPath;

		public void setUploadPath(String uploadPath) {
			this.uploadPath = uploadPath;
		}

		private final Random random = new Random();

		@Override
		public UploadedFile[] inject(HttpServletRequest request, HttpServletResponse res, String paramName) {
			Preconditions.checkNotNull(uploadPath, "uploadPath can not be empty");
			List<UploadedFile> files = new ArrayList<>(1);
			if (ServletFileUpload.isMultipartContent(request)) {
				ServletFileUpload upload = new ServletFileUpload();
				try {
					FileItemIterator it = upload.getItemIterator(request);
					while (it.hasNext()) {
						FileItemStream item = it.next();
						if (paramName.equals(item.getFieldName())) {
							InputStream stream = item.openStream();
							if (!item.isFormField()) {
								String fileName = item.getName();
								int index = fileName.lastIndexOf('.');
								String fileExt = index == -1 ? null : fileName.substring(index);
								String name = System.currentTimeMillis() + random.nextInt(100) + fileExt;
								String path = uploadPath + name;
								Files.copy(stream, Paths.get(path));
								files.add(new UploadedFile(name, path));
							}
							break;
						}
					}
				} catch (FileUploadException | IOException e) {
					throw new RuntimeException("upload File error", e);
				}
			}
			return files.isEmpty() ? null : files.toArray(new UploadedFile[files.size()]);
		}
	},

	FILE {
		@Override
		public UploadedFile inject(HttpServletRequest req, HttpServletResponse res, String paramName) {
			Object result = DefaultInjectors.FILES.inject(req, res, paramName);
			if (result != null) {
				UploadedFile[] files = (UploadedFile[]) result;
				if (files.length == 1) {
					return files[0];
				}
			}
			return null;
		}
	}
}